import 'dart:math';
import 'package:flutter/material.dart';

class Starfield extends StatefulWidget {
  const Starfield({super.key, this.starCount = 60});
  final int starCount;

  @override
  State<Starfield> createState() => _StarfieldState();
}

class _StarfieldState extends State<Starfield>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;
  late final List<_Star> _stars;
  final Random _rand = Random();

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 6),
    )..repeat();
    _stars = List.generate(widget.starCount, (_) => _Star.random());
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: AnimatedBuilder(
        animation: _controller,
        builder: (context, _) {
          return CustomPaint(
            painter: _StarPainter(_stars, _controller.value, _rand),
            size: Size.infinite,
          );
        },
      ),
    );
  }
}

class _Star {
  _Star(this.dx, this.dy, this.size, this.baseOpacity, this.speed);
  factory _Star.random() {
    final r = Random();
    return _Star(
      r.nextDouble(),
      r.nextDouble(),
      1 + r.nextDouble() * 2.2,
      0.35 + r.nextDouble() * 0.65,
      0.02 + r.nextDouble() * 0.04,
    );
  }
  double dx; // 0..1 relative
  double dy; // 0..1 relative
  double size;
  double baseOpacity;
  double speed;
}

class _StarPainter extends CustomPainter {
  _StarPainter(this.stars, this.t, this.rand);
  final List<_Star> stars;
  final double t;
  final Random rand;

  @override
  void paint(Canvas canvas, Size size) {
    final Paint p = Paint()..style = PaintingStyle.fill;
    for (final s in stars) {
      // Twinkle
      final double twinkle =
          0.5 + 0.5 * sin((t * 2 * pi) + s.dx * 10.0 + s.dy * 10.0);
      final double opacity = (s.baseOpacity * twinkle).clamp(0.1, 0.9);
      final Offset pos = Offset(
        s.dx * size.width,
        (s.dy + t * s.speed) % 1.0 * size.height,
      );
      final double r = s.size;

      p.color = Colors.white.withOpacity(opacity);
      canvas.drawCircle(pos, r, p);

      // Glow
      p.maskFilter = const MaskFilter.blur(BlurStyle.normal, 4);
      p.color = Colors.white.withOpacity(opacity * 0.4);
      canvas.drawCircle(pos, r * 1.8, p);
      p.maskFilter = null;
    }
  }

  @override
  bool shouldRepaint(covariant _StarPainter oldDelegate) => true;
}
